---
--- 鉴权处理，token检验，白名单过滤等
--- Created by E.T.
--- DateTime: 2018/11/12 17:41
---
local http_tools = require "kong.plugins.xhg-auth.tools.http_tools"
local string_tools = require "kong.plugins.xhg-auth.tools.string_tools"
local redis_tools = require "kong.plugins.xhg-auth.tools.redis_tools"
local config = require "kong.plugins.xhg-auth.tools.config"
local date_tools = require "kong.plugins.xhg-auth.tools.date_tools"
local cjson = require "cjson"
local des = require "resty.nettle.des"
local str = require "resty.string"
require "kong.plugins.xhg-auth.auth.app_filter"
require "kong.plugins.xhg-auth.auth.whitelist_filter"
require "kong.plugins.xhg-auth.auth.method_filter"
local service = kong.service
local request = kong.request
local log = kong.log

local opts = {
    ttl = config.apps_data_ttl,
}
local conf
local _M = {}

local whitelist_filter = {}

local method_filter = MethodFilter:new()

local function Illegal_token()
    http_tools.response(200, "-2", "当前请求无效")
end

--从body参数里面获取token
local function get_body_token()
    local body, err, mimetype = request.get_body()
    if mimetype == config.mimetype_json then
        return body.requestHead.token
    end
    --多媒体文本 目前没有传token
    --if mimetype == config.mimetype_multipart then
    --    return body.token
    --end
end

--从redis获取用户token value
local function load_user_token(token)
    redis_tools.init(conf.redis_cluster_nodes)
    redis_tools.close()
    --获取customer:user:login:token:?
    local login_token_value = redis_tools.get(config.login_token_kong .. token)
    print("【鉴权】kong:user:login:token:值--- "..login_token_value)
    if login_token_value ~= ngx.null then
        return { k = config.login_token_kong .. token, v = login_token_value }
    end
    return nil, { status = 500, message = "没有获取到token值" }
end

--获取token信息，返回rediskey redisvalue
local function get_token_info(token)
    local value, err = kong.cache:get(config.auth_token_cache_key .. token, opts, load_user_token, token)
    if err then
        log.err("【鉴权】没有从缓存中获取到token用户信息")
        http_tools.response(200, "-2", "您太久没有登录了，请重新登录吧")
    end
    return value["k"], value["v"]
end

-- 异常捕获
function try(block)
    local main = block.main
    local catch = block.catch
    local finally = block.finally
    assert(main)
    -- try to call it
    local ok, errors = xpcall(main, debug.traceback)
    if not ok then
        -- run the catch function
        if catch then
            catch(errors)
        end
    end
    -- run the finally function
    if finally then
        finally(ok, errors)
    end
    -- ok?
    if ok then
        return errors
    end
end

--组装token value
local function encrypt_token_value(key, current_time, next_time, value)
    value.loginTime = current_time
    value.extTime = next_time
    redis_tools.setex(key, (next_time - current_time), cjson.encode(value))
end


--校验token合法性
local function check(key, token_value)
    local value = cjson.decode(token_value)
    --local ds, wk = des.new(config.des_password)
    --local value_str = ds:decrypt(string_tools.hex2bin(token_value))
    if value == nil then
        http_tools.response(200, "-2", "用户信息为空")
    end
    --当前时间戳
    local current_time = os.time()
    --过期时间
    local expire_time = value.extTime
    --剩余天数
    local second = (expire_time - current_time)
    --判断token是否过期
    if second <= 0 or expire_time == -2 then
        http_tools.response(200, "-2", "您太久没有登录了，请重新登录吧")
    end
    --token自动续期(每个业务token续期都不一样)
    if (second <= config.renewal_day and second >= 86400) or (second <= config.renewal_min and second >= 1) then
        log.notice("【鉴权】login_token 续期  ", key)
        local next_time = expire_time + value.renewalTime
        encrypt_token_value(key, current_time, next_time, value)
    end
    --log.notice("通过-> 正常用户,剩余时间", second, "秒")
end

local function do_check(token)
    --首先校验渠道是否合法，由于旧还存在旧的token 所以这里没办法对旧的token进行解析，待全部token更换完成之后才进行token key解析校验渠道，现在暂时对url地址进行应用判别
    --todo
    --从redis获取token信息
    local key, value = get_token_info(token)
    --解析token值
    check(key, value)
    --把token设置进去请求头（兼容老版本，后面会去掉）
    kong.service.request.set_header("token",token)
end

function _M.execute(c)
    ---------------------------初始化基础数据--------------------------------------
    conf = c
    whitelist_filter = WhitelistFilter:new(conf)
    local path = request.get_path()
    --获取context_path
    --local context_path = string_tools.split(path, "/")
    --local app_channel_filter = AppChannelFilter:new(context_path[1])
    ---------------------------过滤应用端-------------------------------------------
    --[[ if app_channel_filter:filter() then
        return
    end]]
    ---------------------------方法过滤-------------------------------------------
    if method_filter:filter() then
        return
    end
    ---------------------------过滤鉴权白名单--------------------------------------
    if whitelist_filter:auth_whitelist_filter(path) then
        return
    end
    local token = request.get_header("token")
    if token == nil then
        token = get_body_token()
    end
    --测试解析token
    --local token_str = string_tools.split(token, "@")
    --local ds, wk = des.new(config.des_password)
    --local value_str = ds:decrypt(string_tools.hex2bin(token_str[2]))
    --log.notice("【鉴权】token解析->", value_str)
    log.notice("【鉴权】请求路径->", path)
    log.notice("【鉴权】请求token->", token)
    ---------------------------过滤需要鉴权但是前端没有传token过来（兼容前期老版本，后面会去掉）--------------------------------------
    if whitelist_filter:auth_no_token_urls_filter(path, token) then
        return
    end
    ---------------------------校验token合法性--------------------------------------
    try {
        main = function()
            local ok, err = do_check(token)
        end,
        catch = function(errors)
            log.err("【鉴权】请求路径->", path)
            log.err("【鉴权】请求token->", token)
            log.err("【鉴权】token解析异常->", errors)
            Illegal_token()
        end,
        finally = function(ok, errors)
        end,
    }
end

return _M